// HidLib.h

#pragma once

#include <windows.h>

extern "C" 
{
	#include <hidsdi.h>
	#include <setupapi.h>
}

#include "HidDeviceEventMonitor.h"
#include "HidDeviceIdentifier.h"
#include "HidDeviceAttributes.h"
#include "HidDeviceCapabilities.h"
#include "HidDeviceData.h"
#include "HidReport.h"

using namespace System;

namespace HidLib 
{
	public ref class HidDevice
	{
	public:
		HidDevice();
		HidDevice(String^ DevicePath, String^ Description);

		enum class DeviceMode
		{
			NonOverlapped = 0,
			Overlapped = 1
		};

		delegate void ReadCallback(HidDeviceData^ Data);
		delegate void ReadReportCallback(HidReport^ Report);
		delegate void WriteCallback(bool Success);

		void Open(DeviceMode _ReadMode, DeviceMode _WriteMode);
		void Open();
		void Read(ReadCallback^ Callback);
		HidDeviceData^ Read();
		HidDeviceData^ Read(UInt32 Timeout);
		HidReport^ ReadReport();
		HidReport^ ReadReport(UInt32 Timeout);
		void ReadReport(ReadReportCallback^ Callback);
		void Close();

		void Write(array<Byte>^ Data, WriteCallback^ Callback);
		bool Write(array<Byte>^ Data);
		bool Write(array<Byte>^ Data, UInt32 Timeout);

		void WriteReport(HidReport^ Report, WriteCallback^ Callback);
		bool WriteReport(HidReport^ Report);
		bool WriteReport(HidReport^ Report, UInt32 Timeout);

		HidReport^ CreateReport();

		event HidDeviceStatusChanged^ OnInsert
		{
			void add(HidDeviceStatusChanged^ Del)
			{
				_OnInsert += Del;
			}
			void remove(HidDeviceStatusChanged^ Del)
			{
				_OnInsert -= Del;
			}
			void raise()
			{
				HidDeviceStatusChanged^ Tmp = _OnInsert;
				if(Tmp)
				{
					Tmp->Invoke();
				}
			}
		}

		event HidDeviceStatusChanged^ OnRemove
		{
			void add(HidDeviceStatusChanged^ Del)
			{
				_OnRemove += Del;
			}
			void remove(HidDeviceStatusChanged^ Del)
			{
				_OnRemove -= Del;
			}
			void raise()
			{
				HidDeviceStatusChanged^ Tmp = _OnRemove;
				if(Tmp)
				{
					Tmp->Invoke();
				}
			}
		}

		property String^ DevicePath
		{
			String^ get()
			{
				return _DevicePath;
			}
		};

		property HidDeviceAttributes^ Attributes
		{
			HidDeviceAttributes^ get()
			{
				return _DeviceAttributes;
			}
		};

		property bool IsConnected
		{
			bool get()
			{
				return _DeviceEventMonitor->Connected;
			}
		};

		property bool IsOpen
		{
			bool get()
			{
				return _DeviceOpen;
			}
		};

	private:
		delegate HidDeviceData^ ReadDelegate();
		delegate HidReport^ ReadReportDelegate();
		delegate bool WriteDelegate(array<Byte>^ Data);
		delegate bool WriteReportDelegate(HidReport^ Report);

		HANDLE OpenDeviceIO(String^ DevicePath1, DWORD DeviceAccess);
		HANDLE OpenDeviceIO(String^ DevicePath1, HidDevice::DeviceMode Mode, DWORD DeviceAccess);

		void CloseDeviceIO(HANDLE HidHandle);

		HidDeviceData^ ReadData(UInt32 Timeout);
		bool WriteData(array<Byte>^ Data, UInt32 Timeout);
		static void EndRead(IAsyncResult^ Ar);
		static void EndReadReport(IAsyncResult^ Ar);

		array<Byte>^ CreateInputBuffer();
		array<Byte>^ CreateOutputBuffer();
		array<Byte>^ CreateBuffer(int BufferLength);

		static void EndWrite(IAsyncResult^ Ar);
		static void EndWriteReport(IAsyncResult^ Ar);

		void Close(bool DisableDeviceMonitor);

		HidDeviceAttributes^ GetDeviceAttributes(HANDLE HidHandle);
		HidDeviceCapabilities^ GetDeviceCapabilities(HANDLE HidHandle);

		HidDeviceEventMonitor^ _DeviceEventMonitor;
		String^ _DevicePath;
		String^ _Description;

		HidDeviceAttributes^ _DeviceAttributes;
		HidDeviceCapabilities^ _DeviceCapabilities;

		DeviceMode _DeviceReadMode;
		DeviceMode _DeviceWriteMode;

		HANDLE _HidReadHandle;
		HANDLE _HidWriteHandle;

		bool _DeviceOpen;

		HidDeviceStatusChanged^ _OnInsert;
		HidDeviceStatusChanged^ _OnRemove;
	};
}
